home *** CD-ROM | disk | FTP | other *** search
/ Stone Design / Stone Design.iso / Stone_Friends / NeXT-Icons / next-icon@gun.com / Apps / ImagePortfolio / threads.subproj / objectThreadPerform.m < prev    next >
Encoding:
Text File  |  1993-06-03  |  12.4 KB  |  394 lines

  1. // -------------------------------------------------------------------------------------
  2. // objectThreadPerform.m
  3. // author: Martin D. Flynn
  4. // (see objectThreadPerform.h for usage information)
  5. // -------------------------------------------------------------------------------------
  6. // Permission is granted to freely redistribute this source code, and to use fragments
  7. // of this code in your own applications if you find them to be useful.  This module,
  8. // along with the source code, come with no warranty of any kind, and the user assumes
  9. // all responsibility for its use.
  10. // -------------------------------------------------------------------------------------
  11.  
  12. #import <objc/objc.h>
  13. #import <appkit/appkit.h>
  14. #import <libc.h>
  15. #import <stdio.h>
  16. #import <c.h>
  17. #import <dpsclient/dpsclient.h>
  18. #import <dpsclient/dpsNeXT.h>
  19. #import <servers/netname.h>
  20. #import <mach/cthreads.h>
  21.  
  22. // -------------------------------------------------------------------------------------
  23. // misc defines
  24. #define    appPortFORMAT    "port_%s"
  25. #define    cpNil            (char*)nil
  26. #define    vpNil            (void*)nil
  27.  
  28. // -------------------------------------------------------------------------------------
  29. // main thread performance information structure
  30.  
  31. /* target/action perform information */
  32. #define    SIGNATURE        0xF121658F
  33. typedef struct targetAction_s {
  34.     long                    sig;            // structure signature
  35.     id                        target;
  36.     SEL                        action;
  37.     id                        arg[2];
  38.     int                        argCount;
  39.     any_t                    result;
  40.     mutex_t                    mutex;
  41.     condition_t                condition;
  42.     BOOL                    wait;
  43.     struct targetAction_s    *next;
  44. } targetAction_t;
  45.  
  46. /* mach port data structure */
  47. typedef struct {
  48.     msg_header_t            hdr;
  49.     msg_type_t                type;
  50.   void                        *data;
  51. } performMessage_t;
  52.  
  53. // -------------------------------------------------------------------------------------
  54. // global static variables
  55. static targetAction_t    *lastDp = (targetAction_t*)nil;        // last chained perform struct
  56. static mutex_t            performMutex = (mutex_t)nil;        // perform mutex
  57. static port_t            performPort = (port_t)nil;            // perform message port
  58. static cthread_t        mainThread = (cthread_t)nil;        // main thread
  59.  
  60. // -------------------------------------------------------------------------------------
  61. @implementation Object(ThreadPerform)
  62.  
  63. // -------------------------------------------------------------------------------------
  64. // target/action structure alloc/free
  65. // -------------------------------------------------------------------------------------
  66.  
  67. /* allocate target/action structure */
  68. static targetAction_t
  69. *allocActionStruct(id self, SEL aSel, id arg0, id arg1, int count, BOOL wait)
  70. {
  71.     targetAction_t    *dp = NXZoneMalloc([self zone], sizeof(targetAction_t));
  72.     dp->sig            = SIGNATURE;
  73.     dp->target        = self;
  74.     dp->action        = aSel;
  75.     dp->arg[0]        = arg0;
  76.     dp->arg[1]        = arg1;
  77.     dp->argCount    = count;
  78.     dp->wait        = wait;
  79.     dp->mutex        = mutex_alloc();
  80.     dp->condition    = condition_alloc();
  81.     dp->result        = (any_t)nil;
  82.     dp->next        = (targetAction_t*)nil;
  83.   return dp;
  84. }
  85.  
  86. /* free target/action structure */
  87. static targetAction_t *freeActionStruct(targetAction_t *dp)
  88. {
  89.     targetAction_t    *next;
  90.     mutex_lock(performMutex);
  91.     next = dp->next;
  92.     if (dp == lastDp) lastDp = (targetAction_t*)nil;
  93.     mutex_unlock(performMutex);
  94.     mutex_free(dp->mutex);
  95.     condition_free(dp->condition);
  96.     free(dp);
  97.     return next;
  98. }
  99.  
  100. // -------------------------------------------------------------------------------------
  101. // thread perform
  102. // -------------------------------------------------------------------------------------
  103.  
  104. /* perform with argCount specified */
  105. - perform:(SEL)selector with:arg1 with:arg2 argCount:(int)argCount
  106. {
  107.     switch (argCount) {
  108.         case 0: return [self perform:selector];                        break;
  109.         case 1: return [self perform:selector with:arg1];            break;
  110.         case 2: return [self perform:selector with:arg1 with:arg2];    break;
  111.     }
  112.     return (id)nil;
  113. }
  114.  
  115. /* perform target/action in structure */
  116. static id performActionStruct(targetAction_t *dp)
  117. {
  118.  
  119.     /* perform method */
  120.     mutex_lock(dp->mutex);
  121.     dp->result = [dp->target perform:dp->action with:dp->arg[0] with:dp->arg[1]
  122.                           argCount:dp->argCount];
  123.   
  124.     /* signal method completion */
  125.     condition_signal(dp->condition);
  126.     mutex_unlock(dp->mutex);
  127.   
  128.     /* return results */
  129.     return (id)dp->result;
  130. }
  131.  
  132. /* port message handler */
  133. static void _performProc(msg_header_t *msg, void *data)
  134. {
  135.     targetAction_t    *dp = ((performMessage_t*)msg)->data;
  136.     while (dp && (dp->sig == SIGNATURE)) {        // loop until no data, or wait
  137.         BOOL wait = dp->wait;                    // wait status save for later checking
  138.         performActionStruct(dp);                // execute action
  139.         if (wait) break;                        // break if waiting for return
  140.         dp = freeActionStruct(dp);                // free and get next structure
  141.     }
  142. }
  143.  
  144. /* port initialization (MUST BE EXECUTE FROM MAIN THREAD ONLY!) */
  145. // - This function will be called automatically from forkPerform:...
  146. #define    doINIT   { if (mainThread == (cthread_t)nil) _doInit(); }
  147. static void _doInit()
  148. {
  149.     char    sName[256];
  150.   
  151.     /* return if already initialized */
  152.     if (mainThread) return;
  153.     mainThread = cthread_self();
  154.   
  155.     /* allocate perform mutex */
  156.     performMutex = mutex_alloc();
  157.  
  158.     /* allocate perform port (port name is made public) */
  159.     sprintf(sName, appPortFORMAT, [NXApp appName]);
  160.     if ((port_allocate(task_self(),&performPort) == KERN_SUCCESS) &&
  161.         (port_set_backlog(task_self(),performPort,PORT_BACKLOG_MAX) == KERN_SUCCESS) &&
  162.         (netname_check_in(name_server_port,sName,PORT_NULL,performPort) == NETNAME_SUCCESS)) {
  163.         DPSAddPort(performPort, _performProc, MSG_SIZE_MAX, vpNil, NX_MODALRESPTHRESHOLD);
  164.     } else {
  165.         NXLogError("objectThreadPerfrom: Unable to allocate port for thread support");
  166.         performPort = (port_t)nil;
  167.         exit(255);
  168.     }
  169.     
  170. }
  171.  
  172. /* explicit initialization (MUST BE EXECUTE FROM MAIN THREAD ONLY!) */
  173. + initThreadSupport
  174. {
  175.     doINIT;
  176.     return self;
  177. }
  178.  
  179. /* return true if calling thread is main thread */
  180. + (BOOL)isMainThread { return (mainThread == cthread_self()); }
  181. - (BOOL)isMainThread { return (mainThread == cthread_self()); }
  182.  
  183. /* return cthread_self() */
  184. - (cthread_t)cthreadSelf { return cthread_self(); }
  185.  
  186. // -------------------------------------------------------------------------------------
  187. // port message perform support
  188. // multiple calls to 'mainThreadPerform:with:wait:' with wait:NO will be chained together
  189. // if the prior call has not completed execution.  This is done to reduce the load on
  190. // mach_port usage.
  191. // -------------------------------------------------------------------------------------
  192.  
  193. /* send perform message to port */
  194. static BOOL _sendPerformToPort(port_t portId, void *data)
  195. {
  196.     performMessage_t     pm;
  197.     msg_return_t        err;
  198.     msg_timeout_t        timeout = 45000;                    // 45 seconds 
  199.  
  200.     /* check for valid port */
  201.     if (!portId) return YES;
  202.   
  203.     /* set up the header */
  204.     pm.hdr.msg_simple            = TRUE;                    // data is inline
  205.     pm.hdr.msg_size                = sizeof(performMessage_t);
  206.     pm.hdr.msg_type                = MSG_TYPE_NORMAL;
  207.     pm.hdr.msg_remote_port         = portId;                // destination port 
  208.     pm.hdr.msg_local_port        = PORT_NULL;
  209.     pm.hdr.msg_id                = 0;                    // receiver message type
  210.  
  211.     /* set up the typeDescriptor */
  212.     pm.type.msg_type_name        = MSG_TYPE_CHAR;
  213.     pm.type.msg_type_size        = 8;                    // 8 bits / byte
  214.     pm.type.msg_type_inline        = TRUE;                    // data is inline
  215.     pm.type.msg_type_number        = sizeof(targetAction_t*);
  216.   
  217.     /* set up the data */
  218.     pm.type.msg_type_longform    = FALSE;
  219.     pm.type.msg_type_deallocate    = FALSE;                // do not deallocate
  220.     pm.data                        = data;                    // the data
  221.  
  222.     /* send message and return results */
  223.     err = msg_send((msg_header_t*)&pm, (msg_option_t)SEND_TIMEOUT, timeout);
  224.     return (err == SEND_SUCCESS)? NO: YES;
  225.   
  226. }
  227.  
  228. /* local mainThreadPerform method */
  229. static id _mainThreadPerform(port_t portId, targetAction_t *dp)
  230. {
  231.     any_t    result;
  232.   
  233.     /* check non-wait request */
  234.     if (!dp->wait) {
  235.         mutex_lock(performMutex);
  236.         mutex_lock(dp->mutex);
  237.         if (lastDp) lastDp->next = dp;
  238.         else _sendPerformToPort(portId, (void*)dp);
  239.         lastDp = dp;
  240.         mutex_unlock(dp->mutex);
  241.         mutex_unlock(performMutex);
  242.         return (id)nil;
  243.     }
  244.  
  245.     /* send message and wait for return */
  246.     mutex_lock(dp->mutex);
  247.     if (!_sendPerformToPort(portId, (void*)dp)) condition_wait(dp->condition, dp->mutex);
  248.     mutex_unlock(dp->mutex);
  249.     result = dp->result;
  250.   
  251.     /* free structure */
  252.     freeActionStruct(dp);
  253.  
  254.     return (id)result;
  255.   
  256. }
  257.  
  258. /* perform selector from main thread (no args) */
  259. - mainThreadPerform:(SEL)aSelector wait:(BOOL)waitForReturn
  260. {
  261.     targetAction_t    *dp;
  262.     doINIT; /* just in case: we better be the main thread if this is executed! */
  263.     if ([self isMainThread]) return [self perform:aSelector];
  264.     dp = allocActionStruct(self, aSelector, (id)nil, (id)nil, 0, waitForReturn);
  265.     return _mainThreadPerform(performPort, dp);
  266. }
  267.  
  268. /* perform selector from main thread (1 arg) */
  269. - mainThreadPerform:(SEL)aSelector with:anArg wait:(BOOL)waitForReturn
  270. {
  271.     targetAction_t    *dp;
  272.     doINIT; /* just in case: we better be the main thread if this is executed! */
  273.     if ([self isMainThread]) return [self perform:aSelector with:anArg];
  274.     dp = allocActionStruct(self, aSelector, anArg, (id)nil, 1, waitForReturn);
  275.     return _mainThreadPerform(performPort, dp);
  276. }
  277.  
  278. /* perform selector from main thread (2 args) */
  279. - mainThreadPerform:(SEL)aSelector with:anArg0 with:anArg1 wait:(BOOL)waitForReturn
  280. {
  281.     targetAction_t    *dp;
  282.     doINIT; /* just in case: we better be the main thread if this is executed! */
  283.     if ([self isMainThread]) return [self perform:aSelector with:anArg0 with:anArg1];
  284.     dp = allocActionStruct(self, aSelector, anArg0, anArg1, 2, waitForReturn);
  285.     return _mainThreadPerform(performPort, dp);
  286. }
  287.  
  288. // -------------------------------------------------------------------------------------
  289. // fork thread support
  290. // -------------------------------------------------------------------------------------
  291.  
  292. /* forked method router */
  293. static void threadRouter(targetAction_t *dp)
  294. {
  295.   
  296.     /* wait here until parent thread is ready */
  297.     mutex_lock(dp->mutex);
  298.     mutex_unlock(dp->mutex);
  299.   
  300.     /* execute thread */
  301.     performActionStruct(dp);                        // execute action
  302.     freeActionStruct(dp);                            // free structure
  303.  
  304.     /* terminate thread */
  305.     cthread_exit(0);                                // terminate thread
  306.   
  307. }
  308.  
  309. /* fork method thread */
  310. - (cthread_t)forkPerform:(SEL)aSelector with:arg0 with:arg1 argc:(int)cnt detach:(BOOL)detach
  311. {
  312.     cthread_t        cthread;
  313.     targetAction_t    *dp = allocActionStruct(self, aSelector, arg0, arg1, cnt, NO);
  314.   
  315.     /* initialize if necessary (for the first time, we better be the main thread!)*/
  316.     doINIT;
  317.   
  318.       /* fork thread */
  319.     mutex_lock(dp->mutex);
  320.     cthread = cthread_fork((cthread_fn_t)threadRouter, (any_t)dp);
  321.     if (detach) cthread_detach(cthread);
  322.     mutex_unlock(dp->mutex);
  323.     cthread_yield();        // allow thread to run
  324.   
  325.     return (detach)? (cthread_t)nil: cthread;        // handle may not be valid if detached
  326. }
  327.  
  328. /* fork method thread */
  329. - (cthread_t)forkPerform:(SEL)aSelector detach:(BOOL)detach
  330. {
  331.     return [self forkPerform:aSelector with:(id)nil with:(id)nil argc:0 detach:detach];
  332. }
  333.  
  334. /* fork method thread */
  335. - (cthread_t)forkPerform:(SEL)aSelector with:anArg detach:(BOOL)detach
  336. {
  337.     return [self forkPerform:aSelector with:anArg with:(id)nil argc:1 detach:detach];
  338. }
  339.  
  340. /* fork method thread */
  341. - (cthread_t)forkPerform:(SEL)aSelector with:anArg0 with:anArg1 detach:(BOOL)detach
  342. {
  343.     return [self forkPerform:aSelector with:anArg0 with:anArg1 argc:2 detach:detach];
  344. }
  345.   
  346. // -------------------------------------------------------------------------------------
  347. // return cthread status
  348. // -------------------------------------------------------------------------------------
  349.  
  350. /* return true if thread is active */
  351. #define    threadMAIN        0x1
  352. #define    threadRETURNED    0x2
  353. #define    threadDETACHED    0x4
  354. - (BOOL)threadIsActive:(cthread_t)theCthread
  355. {
  356.   if (!theCthread) return NO;
  357.   return (theCthread->state & threadRETURNED)? NO: YES;
  358. }
  359.   
  360. // -------------------------------------------------------------------------------------
  361. // terminate thread support
  362. // -------------------------------------------------------------------------------------
  363.  
  364. /* terminate calling thread (may be overridden by subclass) */
  365. - terminateThread
  366. {
  367.   cthread_exit(0);
  368.   return (id)nil;
  369. }
  370.  
  371. /* terminate specified thread */
  372. // IMPORTANT WARNING: Thist method of thread termination should only be considered as a
  373. //    last resort.  This method of thread termination doesn't allow for resource
  374. //    deallocation and cleanup, and could cause unpredictable results.
  375. - terminateThread:(cthread_t)theCthread
  376. {
  377.   
  378.   /* check for invalid cthread handle */
  379.   if (!theCthread) return (id)nil;
  380.   
  381.   /* check for terminating cthread_self() */
  382.   if (theCthread == cthread_self()) {
  383.     cthread_detach(theCthread);
  384.     return [self terminateThread];
  385.   }
  386.  
  387.   /* this method is not really safe, but it does seem to work */
  388.   thread_terminate(cthread_thread(theCthread));
  389.   
  390.   return self;
  391. }
  392.  
  393. @end
  394.